package com.habdqn.studentmanager.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;
import javax.transaction.Transactional;

import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import com.github.pagehelper.PageInfo;
import com.habdqn.studentmanager.bean.Classes;
import com.habdqn.studentmanager.bean.Dictionary;
import com.habdqn.studentmanager.bean.Student;
import com.habdqn.studentmanager.bean.Teacher;
import com.habdqn.studentmanager.bean.ajax.Page;
import com.habdqn.studentmanager.bean.condition.BigDecimalCondition;
import com.habdqn.studentmanager.bean.condition.DateCondition;
import com.habdqn.studentmanager.bean.score.ClassScore;
import com.habdqn.studentmanager.bean.score.ClassScoreInfo;
import com.habdqn.studentmanager.bean.score.Score;
import com.habdqn.studentmanager.bean.score.StudentPoint;
import com.habdqn.studentmanager.bean.score.StudentScore;
import com.habdqn.studentmanager.bean.score.req.ClassScoreCondition;
import com.habdqn.studentmanager.constant.DictType;
import com.habdqn.studentmanager.constant.LogType;
import com.habdqn.studentmanager.exception.StudentException;
import com.habdqn.studentmanager.mapper.ScoreMapper;
import com.habdqn.studentmanager.util.ConfigUtil;
import com.habdqn.studentmanager.util.DateUtil;
import com.habdqn.studentmanager.util.ExcelUtil;
import com.habdqn.studentmanager.util.ExcelUtil.Excel.Row;
import com.habdqn.studentmanager.util.MyBatisParam;
import com.habdqn.studentmanager.util.ProjectUtil;
import com.habdqn.studentmanager.util.SystemLogger;

/**
 * 成绩服务类
 * 
 * @author zhxml
 * 
 *         2017年7月18日
 *
 */
@Service
public class ScoreService extends BaseService {

    @Resource
    private ScoreMapper scoreMapper;

    @Resource
    private StudentService studentService;

    @Resource
    private ClassesService classService;

    @Resource
    private DictionaryService dictService;
    
    @Resource
    private SystemLogger sysLogger;
    
    /**
     * 查询成绩列表
     * 
     * @param bean
     * @param page
     * @return
     */
    public PageInfo<Score> findList(Score bean, Page page, DateCondition scoreDate, BigDecimalCondition scorePoint) {

        try {
            startPage(page);
            List<Score> lists = scoreMapper.findList(bean, scoreDate, scorePoint);
            return buildPageInfo(lists);
        }
        catch (RuntimeException e) {
            logger.debug(e.getMessage(), e);
            throw new StudentException("查询成绩列表失败", e);
        }
    }

    /**
     * 
     * @param bean
     */
    public void add(Score bean) {
    	Teacher current = ProjectUtil.getCurrentUser();
		bean.setTeacher(current);
        try {
            scoreMapper.insert(bean);
        }
        catch (DuplicateKeyException e) {
            logger.debug(e.getMessage(), e);
            throw buildException("成绩重复", e);
        }
        catch (RuntimeException e) {
            logger.debug(e.getMessage(), e);
            throw buildException("新增成绩失败", e);
        }
        sysLogger.record(current, LogType.AddType, "添加成绩,编号："+bean.getId());
    }

    /**
     * 删除成绩
     * 
     * @param bean
     */
    public void delete(Score bean) {

        try {
            int ret = scoreMapper.delete(bean);
            if (ret != 1) {
                logger.debug("影响行数不为1.实际:{}", ret);
                throw new RuntimeException();
            }
        }
        catch (RuntimeException e) {
            logger.debug(e.getMessage(), e);
            throw buildException("删除成绩失败", e);
        }

    }

    /**
     * 更新成绩信息
     * 
     * @param bean
     */
    public void update(Score bean) {
    	Teacher current = ProjectUtil.getCurrentUser();
		bean.setTeacher(current);
        try {
            int ret = scoreMapper.update(bean);
            if (ret != 1) {
                logger.debug("影响行数不为1.实际:{}", ret);
                throw new RuntimeException();
            }
        }
        catch (RuntimeException e) {
            logger.debug(e.getMessage(), e);
            throw buildException("更新成绩失败", e);
        }
        sysLogger.record(current, LogType.AddType, "更新成绩,编号："+bean.getId());
    }

    /**
     * 查询成绩信息
     * 
     * @param bean
     */
    public Score find(Score bean) {

        try {
            return scoreMapper.find(bean);
        }
        catch (RuntimeException e) {
            logger.debug(e.getMessage(), e);
            throw buildException("查询成绩失败", e);
        }
    }

    /**
     * 获取学生概览页信息
     * 
     * @param bean
     * @return
     */
    public StudentScore getStudentDashBoard(Student bean) {

        bean = studentService.find(bean);
        if (null == bean) {
            throw buildException("获取学员信息失败");
        }

        Score score = new Score();
        score.setStudent(bean);

        PageInfo<Score> pageInfo = findList(score, null, null, null);

        if (null == pageInfo || null == pageInfo.getList()) {
            return null;
        }

        List<Score> scores = pageInfo.getList();
        StudentScore studentScore = new StudentScore();
        studentScore.setStudent(bean);
        float passRate = 0;
        for (Score item : scores) {

            String content = item.getContent();

            // 如果考试标题为空，则用考试日期代替
            if (StringUtils.isEmpty(content)) {
                item.setContent(DateUtil.format(item.getDate(), DateUtil.FormatStr10));
            }

            Double point = item.getPoint();
            Double passScore = item.getPassScore();

            if (null == point || null == passScore) {
                continue;
            }

            if (point >= passScore) {
                passRate += 1;
            }
        }

        // 计算及格率
        if (0 == scores.size()) {
            passRate = 0;
        }
        else {
            passRate = passRate / scores.size() * 100;
        }
        studentScore.setPassRate(passRate);

        // 获取前7条数据, 并按照从小到大的顺序排列
        int topSize = 7;
        if (scores.size() < 7) {
            topSize = scores.size();
        }

        List<Score> topScores = new ArrayList<>(scores.subList(0, topSize));
        Collections.reverse(topScores);
        studentScore.setScores(scores);
        studentScore.setTopScores(topScores);

        return studentScore;
    }

    /**
     * 查询班级成绩走向
     * 
     * @param param
     * @return
     */
    public List<ClassScore> findClassScoreTrend(Classes classes) {

        List<ClassScore> classScores = null;
        try {
            logger.debug("class:{}", classes);
            classes = classService.find(classes);

            // 展示最近7天
            startPage(Page.build(1, 7));
            MyBatisParam param = new MyBatisParam();
            param.put("className", classes.getName());
            classScores = scoreMapper.findClassScoreTrend(param);
        }
        catch (StudentException e) {
            throw e;
        }
        catch (Exception e) {
            logger.error("查询班级成绩走向列表失败", e);
            throw buildException("查询成绩列表失败", e);
        }

        // 当考试内容为空时，用考试日期代替
        for (ClassScore classScore : classScores) {
            String content = classScore.getContent();
            if (StringUtils.isEmpty(content)) {
                classScore.setContent(DateUtil.format(classScore.getDate(), DateUtil.FormatStr10));
            }
        }

        // 展示的时候需要从小到大展示
        Collections.reverse(classScores);
        return classScores;
    }

    /**
     * 根据班级查询学生最高分，最低分，平均分列表
     * 
     * @param param
     */
    public List<StudentPoint> findStudentScoreByClass(Classes classes) {

        try {
            logger.debug("class:{}", classes);
            classes = classService.find(classes);

            MyBatisParam param = new MyBatisParam();
            param.put("className", classes.getName());
            List<StudentPoint> studentPoints = scoreMapper.findStudentScoreByClass(param);
            return studentPoints;
        }
        catch (StudentException e) {
            throw e;
        }
        catch (Exception e) {
            logger.error("根据班级查询学生成绩列表失败", e);
            throw buildException("查询成绩列表失败", e);
        }
    }

    /**
     * 批量导出成绩记录
     * 
     * @param bean
     * @param out
     */
    public void export(Score bean, DateCondition scoreDate, BigDecimalCondition scorePoint, OutputStream out) {
        // 获取模板文件的路径
        String path = ProjectUtil.getRealPath() + ConfigUtil.getKey("excelDemoPath") + File.separator
                + "score-template.xlsx";
        try(InputStream is = new FileInputStream(path)) {
            PageInfo<Score> page = findList(bean, null, scoreDate, scorePoint);
            
            ExcelUtil.init(is).sheetAt(0).rowAt(2).export(page.getList(), out, (Score t, Row row) -> {
                Student stu = t.getStudent();
                row.createCell(0).setValue(stu.getId());
                row.createCell(1).setValue(stu.getName());
                row.createCell(2).setValue(stu.getClasses().getName());
                row.createCell(3).setValue(t.getContent());
                
                Date date = t.getDate();
                String dateStr = DateUtil.format(date, DateUtil.FormatStr10);
                row.createCell(4).setValue(dateStr);
                row.createCell(5).setValue(t.getType().getName());
                row.createCell(6).setValue(t.getPattern().getName());
                row.createCell(7).setValue(t.getPoint() + "");
                row.createCell(8).setValue(t.getPassScore() + "");
                row.createCell(9).setValue(t.getTotalScore() + "");
            }).close();
        }
        catch (RuntimeException e) {
            logger.error("成绩导出代码有问题", e);
            throw buildException("导出失败", e);
        }
        catch (IOException e) {
            logger.debug("操作成绩导出文件失败", e);
            throw buildException("导出失败", e);
        }
    }
    
    /**
     * 批量导入学生成绩
     * @param is
     */
    @Transactional
    public void importScore(InputStream is) {
        ExcelUtil.init(is).sheetAt(0).rowAt(2).accept((Row row) -> {
            // 从1开始的行号
            int rowNum = row.getRowAt() + 1;
            
            try {
                accept(row);
            }
            catch (RuntimeException e) {
                logger.debug("导入失败", e);
                throw buildException("第" + rowNum + "行:" + e.getMessage(), e);
            }
        }).close();
        
        sysLogger.record(LogType.AddType, "批量导入学生成绩");
    }

    /**
     * 处理导入的数据
     * @param row
     */
    private void accept(Row row) {
        
        Score bean = new Score();
        Student stu = new Student();
        stu.setId(row.getCell(0).getValue());
        stu = studentService.find(stu);
        if (null == stu) {
            throw buildException("该学生不存在");
        }
        bean.setStudent(stu);
        
        bean.setContent(row.getCell(1).getValue());
        
        Date date = DateUtil.parseDate(row.getCell(2).getValue(), DateUtil.FormatStr10);
        if (null == date) {
            throw buildException("日期格式不正确");
        }
        bean.setDate(date);
        
        // 考试类型
        String type = row.getCell(3).getValue();
        Integer typeId = null;
        try {
            typeId = dictService.findValue(DictType.ScoreType.toInteger(), type);
        }
        catch (StudentException e) {
            logger.debug("考试类型:{}", type, e);
            throw buildException("考试类型错误", e);
        }
        bean.setType(Dictionary.make(null, null, typeId));
        
        // 考试方式
        String pattern = row.getCell(4).getValue();
        Integer patternId = null;
        try {
            patternId = dictService.findValue(DictType.ScorePattern.toInteger(), pattern);
        }
        catch (StudentException e) {
            logger.debug("考试方式:{}", pattern, e);
            throw buildException("考试方式错误", e);
        }
        bean.setPattern(Dictionary.make(null, null, patternId));
        
        String pointStr = row.getCell(5).getValue();
        Double point = null;
        try {
            point = Double.valueOf(pointStr);
            if (point < 0) {
                throw new RuntimeException();
            }
        }
        catch (RuntimeException e) {
            logger.debug("考试成绩:{}", pointStr, e);
            throw buildException("考试成绩非法", e);
        }
        bean.setPoint(point);
        
        String passPointStr = row.getCell(6).getValue();
        Double passPoint = null;
        try {
            passPoint = Double.valueOf(passPointStr);
            if (passPoint < 0) {
                throw new RuntimeException();
            }
        }
        catch (RuntimeException e) {
            logger.debug("及格成绩:{}", passPointStr, e);
            throw buildException("及格成绩非法", e);
        }
        bean.setPassScore(passPoint);
        
        String totalPointStr = row.getCell(7).getValue();
        Double totalPoint = null;
        try {
            totalPoint = Double.valueOf(totalPointStr);
            if (totalPoint < 0) {
                throw new RuntimeException();
            }
        }
        catch (RuntimeException e) {
            logger.debug("总分:{}", totalPointStr, e);
            throw buildException("总分非法", e);
        }
        bean.setTotalScore(totalPoint);
        
        bean.setTeacher(ProjectUtil.getCurrentUser());
        bean.setCreateDate(new Date());
        
        add(bean);
    }
    
    /**
     * 
     * @param bean
     * @param page
     * @return
     */
    public PageInfo<ClassScoreInfo> findClassScoreList(ClassScoreCondition bean, Page page) {
        
        try {
            MyBatisParam param = new MyBatisParam();
            
            if (null != bean) {
                param.putParam("className", bean.getName());                
            }
            
            startPage(page);
            return new PageInfo<ClassScoreInfo>(scoreMapper.findClassScoreList(param));
        }
        catch (RuntimeException e) {
            logger.debug("查询班级成绩列表失败", e);
            throw buildException("查询班级成绩失败", e);
        }
    }
}